package com.yzh.dbrouter.dynamic;

import com.yzh.dbrouter.DBContextHolder;
import com.yzh.dbrouter.annotation.DBRouterStrategy;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Mybatis 拦截器，通过对SQL语句拦截，修改分表信息
 */
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class DynamicMybatisPlugin implements Interceptor {
    /**
     * 匹配from/into/update后面的表名
     * \\s：忽略空白字符1个或多个
     * \\w：匹配单词字符1个或多个
     * Pattern.CASE_INSENSITIVE：忽略大小写
     */
    private Pattern pattern = Pattern.compile("(from|into|update)[\\s]{1,}(\\w{1,})", Pattern.CASE_INSENSITIVE);

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        //获取被拦截的对象，也就是 StatementHandler 实例，它是是 MyBatis 中执行 SQL 语句的核心接口之一
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        //创建一个 MetaObject 对象，用于操作 statementHandler 对象。MetaObject 可以方便地获取和设置对象的属性值
        MetaObject metaObject = MetaObject.forObject(
                statementHandler,
                SystemMetaObject.DEFAULT_OBJECT_FACTORY,
                SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,
                new DefaultReflectorFactory());
        //获取 statementHandler 对象中的 mappedStatement 属性值，mappedStatement表示映射到 SQL 语句的配置信息
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        //获取自定义注解判断是否进行分表操作
        /*
         * id = com.yzh.luckydraw.infrastructure.dao.IUserStrategyExportDao.insert
         */
        String id = mappedStatement.getId();
        /*
         * 截取出 SQL 语句对应的类名(从0开始，截取到最后的“点”之前)
         * className = com.yzh.luckydraw.infrastructure.dao.IUserStrategyExportDao
         */
        String className = id.substring(0, id.lastIndexOf("."));
        //反射创建对象
        Class<?> clazz = Class.forName(className);
        //获取类注解@DBRouterStrategy
        DBRouterStrategy dbRouterStrategy = clazz.getAnnotation(DBRouterStrategy.class);
        //注解为空或为参数为false则正常执行
        if (dbRouterStrategy == null || !dbRouterStrategy.splitTable()) {
            return invocation.proceed();
        }

        //获取SQL
        BoundSql boundSql = statementHandler.getBoundSql();
        //sql = INSERT INTO user_strategy_export(...) VALUES(...)
        String sql = boundSql.getSql();
        //替换SQL表名
        Matcher matcher = pattern.matcher(sql);
        //tableName = INTO user_strategy_export
        String tableName = null;
        if (matcher.find()) {
            tableName = matcher.group().trim();
        }
        assert null != tableName;
        //replaceSQL = INSERT INTO user_strategy_export_000(...) VALUES(...)
        String replaceSQL = matcher.replaceAll(tableName + "_" + DBContextHolder.getTbKey());

        //通过反射修改SQL语句
        Field field = boundSql.getClass().getDeclaredField("sql");
        field.setAccessible(true);
        field.set(boundSql, replaceSQL);
        field.setAccessible(false);

        return invocation.proceed();

    }
}
